﻿using System;
using ServiceStack.Redis;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using CzfRow = System.Collections.Generic.Dictionary<string, string>;

namespace CzfSDK
{
	public class CzfRedisClient : DBClient
	{
		public RedisClient redis;
		public CzfRedisClient (RedisClient redis, string db)
		{
			this.redis = redis;
			this.db = db;
		}

		public void Close()
		{
			if (redis != null)
				redis.Shutdown ();
		}

		#region DBClient implementation

		public string db { get; set; }

		public void RemoveTable<T> () where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			redis.RemoveTable (db, tableName);
		}

		public List<CzfRow> GetAllRows<T> () where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			return redis.GetAllRows (db, tableName);
		}

		public List<T> GetAllModels<T> () where T : CzfModel, new()
		{
			var rows = GetAllRows<T> ();
			if (rows == null || rows.Count == 0)
				return null;

			var ret = new List<T> (rows.Count);
			foreach (var row in rows) {
				var t = new T ();
				t.Import (row);
				ret.Add (t);
			}
			return ret;
		}
		public bool CreateDb (string db = null)
		{
			if (db == null)
				db = this.db;
			return redis.CreateDb (db);
		}

		public CzfRow GetRow<T> (string indexKeys, string indexVals) where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			return redis.GetRowByIndex (db, tableName, indexKeys, indexVals);
		}

		public T GetModel<T> (string indexKeys, string indexVals) where T : CzfModel, new()
		{
			var row = GetRow<T> (indexKeys, indexVals);
			if (row == null)
				return null;

			var ret = new T();
			ret.Import (row);
			return ret;
		}

		public List<CzfRow> GetRows<T> (string indexKeys, string indexVals) where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			return redis.GetRowsByIndex (db, tableName, indexKeys, indexVals);
		}

		public List<T> GetModels<T> (string indexKeys, string indexVals) where T : CzfModel, new()
		{
			var rows = GetRows<T>(indexKeys, indexVals);
			if (rows == null || rows.Count == 0)
				return null;

			var ret = new List<T> (rows.Count);
			foreach (var row in rows) {
				var t = new T ();
				t.Import (row);
				ret.Add (t);
			}
			return ret;
		}

		public bool CreateTable<T> () where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			var fields = type.GetFields (BindingFlags.Static);

			var indexs = new List<string> ();
			foreach (var field in fields) {
				var attr = field.CzfGetAttribute<CzfIndexAttribute> ();
				if (attr != null) {
					indexs.Add (field.GetValue (null).ToString());
				}
			}

			return redis.CreateTable (db, tableName, indexs.ToArray ());
		}

		public CzfRow GetRow<T> (string id) where T : CzfModel
		{
			var type = typeof(T);
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			return redis.GetRow (db, tableName, id);
		}
		public T GetModel<T> (string id) where T : CzfModel, new()
		{
			var row = GetRow<T> (id);
			if (row == null)
				return null;
			var ret = new T ();
			ret.Import (row);
			return ret;
		}
		public void Update (CzfModel m)
		{
			var type = m.GetType();
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			var row = m.ExportCzfRow ();
			redis.UpdateRow (db, tableName, row);
		}
		public void Create (CzfModel m)
		{
			var type = m.GetType();
			var tableName = type.CzfGetAttribute<CzfTableAttribute> ().tableName;
			var row = m.ExportCzfRow ();
			var newRow = redis.CreateRow (db, tableName, row);
			m.Import (newRow);
		}
		#endregion
	}
}

